package service

import (
	"context"
	"net/http"
	"time"

	"github.com/go-kratos/kratos/v2/errors"
	"github.com/go-kratos/kratos/v2/log"

	pb "gitee.com/danqingzhao/go-dango/api/user/role/v1"
	"gitee.com/danqingzhao/go-dango/apps/user/biz"
	"gitee.com/danqingzhao/go-dango/pkg/common/core"
	"gitee.com/danqingzhao/go-dango/pkg/proto/settings"
)

type RoleService struct {
	pb.UnimplementedRoleServer
	log    *log.Helper
	ucRole *biz.RoleUsecase
}

func NewRoleService(uc *biz.RoleUsecase, logHelpers *settings.LogHelpers) *RoleService {
	return &RoleService{ucRole: uc, log: logHelpers.Server}
}

func (s *RoleService) CreateRole(
	ctx context.Context,
	req *pb.CreateRoleRequest,
) (*pb.RoleReply, error) {
	im := biz.RoleModel{
		Name:     req.Name,
		IsActive: req.IsActive,
		Descr:    req.Descr,
	}
	om, err := s.ucRole.CreateRole(ctx, req.PermissionIds, req.MenuIds, req.ButtonIds, im)
	if err != nil {
		return nil, err
	}
	data, err := RoleSchemasOut(ctx, s.log, *om)
	return &pb.RoleReply{Code: http.StatusCreated, Message: "", Metadata: data}, err
}

func (s *RoleService) UpdateRole(
	ctx context.Context,
	req *pb.UpdateRoleRequest,
) (*pb.RoleReply, error) {
	im := biz.RoleModel{
		Id:       req.Pk,
		Name:     req.Name,
		IsActive: req.IsActive,
		Descr:    req.Descr,
	}
	if req.PermissionIds == nil {
		req.PermissionIds = []uint32{}
	}
	if req.MenuIds == nil {
		req.MenuIds = []uint32{}
	}
	if req.ButtonIds == nil {
		req.ButtonIds = []uint32{}
	}
	om, err := s.ucRole.UpdateRoleById(ctx, req.Pk, req.PermissionIds, req.MenuIds, req.ButtonIds, im)
	if err != nil {
		return nil, err
	}
	data, err := RoleSchemasOut(ctx, s.log, *om)
	return &pb.RoleReply{Code: http.StatusCreated, Message: "", Metadata: data}, err
}

func (s *RoleService) DeleteRole(
	ctx context.Context,
	req *pb.DeleteRoleRequest,
) (*pb.RoleReply, error) {
	err := s.ucRole.DeleteRoleById(ctx, req.Pk)
	if err != nil {
		return nil, err
	}
	return &pb.RoleReply{Code: http.StatusOK, Message: "", Metadata: nil}, nil
}

func (s *RoleService) GetRole(
	ctx context.Context,
	req *pb.GetRoleRequest,
) (*pb.RoleReply, error) {
	om, err := s.ucRole.FindRoleByID(ctx, []string{"Permissions", "Menus", "Buttons"}, req.Pk)
	if err != nil {
		return nil, err
	}
	data, err := RoleSchemasOut(ctx, s.log, *om)
	return &pb.RoleReply{Code: http.StatusCreated, Message: "", Metadata: data}, err
}

func (s *RoleService) ListRole(
	ctx context.Context,
	req *pb.ListRoleRequest,
) (*pb.PagRoleReply, error) {
	page, size, query := s.filterRole(req)
	count, ms, err := s.ucRole.ListRole(ctx, page, size, query)
	if err != nil {
		return nil, err
	}
	mos, err := ListRoleModelToSchemasOutBase(ctx, s.log, ms)
	if err != nil {
		return nil, err
	}
	rsp := pb.PagRoleOutBase{
		Items: mos,
		Page:  int64(page),
		Pages: core.CountPages(count, int64(size)),
		Size:  int64(size),
		Total: count,
	}
	return &pb.PagRoleReply{Code: http.StatusOK, Message: "", Metadata: &rsp}, nil
}

func (uc *RoleService) filterRole(
	req *pb.ListRoleRequest,
) (int, int, map[string]interface{}) {
	var (
		page  int                    = core.DefaultPage
		size  int                    = core.DefaultSize
		query map[string]interface{} = make(map[string]interface{})
	)
	if req != nil {
		if req.Page > 0 {
			page = int(req.Page)
		}
		if req.Size > 0 {
			size = int(req.Size)
		}
		if req.Pk != 0 {
			query["id = ?"] = req.Pk
		} else if req.Pks != "" {
			pks := core.StringToListUint32(req.Pks)
			if len(pks) > 1 {
				query["id in ?"] = pks
			}
		}
		if req.BeforeCreateTime != "" {
			t, err := time.Parse(time.RFC3339, req.BeforeCreateTime)
			if err == nil {
				query["create_time < ?"] = t
			}
		}
		if req.AfterCreateTime != "" {
			t, err := time.Parse(time.RFC3339, req.BeforeCreateTime)
			if err == nil {
				query["create_time > ?"] = t
			}
		}
		if req.BeforeUpdateTime != "" {
			t, err := time.Parse(time.RFC3339, req.BeforeCreateTime)
			if err == nil {
				query["update_time < ?"] = t
			}
		}
		if req.AfterUpdateTime != "" {
			t, err := time.Parse(time.RFC3339, req.BeforeCreateTime)
			if err == nil {
				query["update_time > ?"] = t
			}
		}
		if req.Name != "" {
			query["name like ?"] = req.Name
		}
		if req.IsActive != nil {
			query["is_active = ?"] = req.IsActive
		}
	}
	return page, size, query
}

func RoleSchemasOutBase(
	ctx context.Context,
	logHelper *log.Helper,
	m biz.RoleModel,
) *pb.RoleOutBase {
	return &pb.RoleOutBase{
		Id:          m.Id,
		CreatedTime: m.CreatedAt.String(),
		UpdatedTime: m.UpdatedAt.String(),
		Name:        m.Name,
		IsActive:    m.IsActive,
		Descr:       m.Descr,
	}
}

func RoleSchemasOut(
	ctx context.Context,
	logHelper *log.Helper,
	m biz.RoleModel,
) (*pb.RoleOut, *errors.Error) {
	pmso := ListPermissionModelToSchemasOutBase(ctx, logHelper, m.Permissions)
	mmso, err := ListMenuModelToSchemasOutBase(ctx, logHelper, m.Menus)
	if err != nil {
		return nil, err
	}
	bmso, err := ListButtonModelToSchemasOutBase(ctx, logHelper, m.Buttons)
	if err != nil {
		return nil, err
	}
	return &pb.RoleOut{
		Id:          m.Id,
		CreatedTime: m.CreatedAt.String(),
		UpdatedTime: m.UpdatedAt.String(),
		Name:        m.Name,
		IsActive:    m.IsActive,
		Descr:       m.Descr,
		Permissions: pmso,
		Menus:       mmso,
		Buttons:     bmso,
	}, nil
}

func ListRoleModelToSchemasOutBase(
	ctx context.Context,
	logHelper *log.Helper,
	ms []biz.RoleModel,
) ([]*pb.RoleOutBase, *errors.Error) {
	mso := make([]*pb.RoleOutBase, 0, len(ms))
	if len(ms) > 0 {
		for _, m := range ms {
			mo := RoleSchemasOutBase(ctx, logHelper, m)
			mso = append(mso, mo)
		}
	}
	return mso, nil
}
